//==============================================================================
//
//         DFraction - the fraction number class in the ofc-library
//
//               Copyright (C) 2003-2005  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2005-06-13 16:13:53 $ $Revision: 1.3 $
//
//==============================================================================

#include "ofc/DFraction.h"

#include <stdlib.h>


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DTextable.h"
#include "ofc/DSimpleArithmetic.h"
#include "ofc/DComparable.h"

#include "ofc/DText.h"

//
// The fraction class implements a number of methods for working with fractions.
//

@interface DFraction : Object <DTextable,DSimpleArithmetic,DComparable>
{
@private
  int          _num;   // the numerator
  int          _denom; // the denominator
}

#endif




@implementation DFraction


//// Constructors

//
// Initialise a zero fraction
//
// @return the object
//

- (DFraction *) init
{
  [super init];

  _num   = 0;
  _denom = 1;
  
  return self;
}

//
// Initialise a fraction
//
// @param num      the numerator
// @param denom    the denominator
//
// @return the object
//

- (DFraction *) init :(int) num :(int) denom
{
  [self init];

  [self set :num :denom];
  
  return self;
}



//// Member methods

//
// Get the numerator
//
// @return the numerator
//

- (int) numerator
{
  return _num;
}

//
// Set the numerator
//
// @param num      the numerator of the fraction
//
// @return the object
//

- (DFraction *) numerator :(int) num
{
  _num = num;

  [self norm];
  
  return self;
}

//
// Get the denominator
//
// @return the denominator
//

- (int) denominator
{
  return _denom;
}

//
// Set the denominator of the fraction
//
// @param denom    the denominator
//
// @return the object
//

- (DFraction *) denominator :(int) denom
{
  if (denom != 0)
  {
    _denom = denom;
    
    [self norm];
  }
  else
  {
    WARNING(DW_INVALID_ARG, "denom");
  }

  
  return self;
}

//
// Set the numerator and denominator of the fraction
//
// @param num      the numerator
// @param denom    the denominator
//
// @return the object
//

- (DFraction *) set :(int) num :(int) denom
{
  if (denom != 0)
  {
    _num   = num;
    _denom = denom;

    [self norm];
  }
  else
  {
    WARNING(DW_INVALID_ARG, "denom");
  }
  
  return self;
}

//
//
// Move an other object in this object
//
// @param other    the other object
//
// @return this object
//

- (DFraction *) move :(DFraction *) other
{
  _num   = [other numerator  ];
  _denom = [other denominator];

  return self;
}



//// Methods on fractions

//
// Add with an other fraction
//
// @param other    the fraction
//
// @return the object
//

- (DFraction *) add :(DFraction *) other
{
  int num   = [other numerator];
  int denom = [other denominator];
  
  if (denom == _denom)
  {
    _num += num;
  }
  else
  { 
    _num   = _num * denom + num * _denom;
    _denom = denom * _denom;
  }
  
  [self norm];
  
  return self;
}

//
// Add two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//

- (DFraction *) add :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    if (fr1->_denom == fr2->_denom)
    {
      _num   = fr1->_num + fr2->_num;
      _denom = fr1->_denom;
    }
    else
    { 
      _num   = fr1->_num * fr2->_denom + fr2->_num * fr1->_denom;
      
      _denom = fr1->_denom * fr2->_denom;
    }
  
    [self norm];
  }
  
  return self;
}

//
// Subract with an other fraction
//
// @param other     the fraction
//
// @return the object
//

- (DFraction *) sub :(DFraction *) other
{
  int num   = [other numerator  ];
  int denom = [other denominator];
  
  if (denom == _denom)
  {
    _num -= num;
  }
  else
  { 
    _num   = _num * denom - num * _denom;
    _denom = denom * _denom;
  }
  
  [self norm];
  
  return self;
}

//
// Subtract two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//

- (DFraction *) sub :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    if (fr1->_denom == fr2->_denom)
    {
      _num   = fr1->_num - fr2->_num;
      _denom = fr1->_denom;
    }
    else
    { 
      _num   = fr1->_num * fr2->_denom - fr2->_num * fr1->_denom;
      
      _denom = fr1->_denom * fr2->_denom;
    }
  
    [self norm];
  }
  
  return self;
}

//
// Multiply with an other fraction
//
// @param other    the other fraction
//
// @return the object
//

- (DFraction *) mul :(DFraction *) other
{ 
  _num   *= [other numerator  ];
  _denom *= [other denominator];
  
  [self norm];

  return self;
}

//
// Multiply two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//

- (DFraction *) mul :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    _num   = fr1->_num   * fr2->_num;
    _denom = fr1->_denom * fr2->_denom;
    
    [self norm];
  }
  
  return self;
}

//
// Divide with an other fraction
//
// @param other    the divider
//
// @return the object
//
- (DFraction *) div :(DFraction *) other
{
  int num = [other numerator];
  
  if (num != 0)
  {
    _num   *= [other denominator];
    _denom *= num;
  }
  else
  {
    WARNING(DW_UNEXPECTED_ERROR, "division by zero");
  }
  
  [self norm];

  return self; 
}

//
// Divide two fractions and store the result in the object
//
// @param fraction the fraction
// @param divider  the divider
//
// @return the object
//
- (DFraction *) div :(DFraction *) fraction :(DFraction *) divider
{
  if ((fraction == nil) || (divider == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fraction/divider");
  }
  else
  {
    if (divider->_num != 0)
    {
      _num   = fraction->_num * divider->_denom;
      _denom = fraction->_denom * divider->_num;
      
      [self norm];
    }
    else
    {
      WARNING(DW_UNEXPECTED_ERROR, "division by zero");
    }
  }

  return self; 
}

//
// Normalize the fraction
//
// @return the object
//

- (DFraction *) norm
{
  int nom = [DFraction gcd :_num :_denom];
  
  // normalize
  _num   /= nom;
  _denom /= nom;

  // keep only numerator negative
  if (_denom < 0)
  {
    _num   = -_num;
    _denom = -_denom;
  }
  
  return self;
}

//
// Convert to a double
//
// @return the double
//

- (double) toDouble
{
  return (double) _num / (double) _denom;
}

//
// Invert the fraction
//
// @return the object
//

- (DFraction *) invert
{
  if (_num != 0)
  {
    int tmp = _num;
    
    _num   = _denom;
    _denom = tmp;
    
    [self norm];
  }
  else
  {
    WARNING(DW_UNEXPECTED_ERROR, "division by zero");
  }

  return self;
}

//// Class methods

//
// Calculate the Greatest Common Divider
// 
// @param a        the first number
// @param b        the second number
// 
// @return the greatest common divider
// 
+ (int) gcd :(int) a :(int) b
{
  int r = 0;
  
  a = abs(a);
  b = abs(b);
  
  if (b > a)
  {
    int t = a; a = b; b = t;
  }
  
  do
  {
    r = a % b;
    
    if (r > 0)
    {
      a = b;
      b = r;
    }
  }
  while (r > 0);
  
  return b;
}

//
// Calculate the Least Common Multiplier
// 
// @param a        the first number
// @param b        the second number
// 
// @return the least common multiplier
// 
+ (int) lcm :(int) a :(int) b
{
  return (a * b) / [DFraction gcd :a :b];
}



//// Textable protocol methods

//
// Convert to a text string 
//
// @return a (new) text string with the complex number
//

- (DText *) toText
{
  DText *str = [[DText alloc] init];
  
  int full = _num / _denom;
  int rem  = abs(_num) % _denom;
  
  if (full == 0)
  {
    if (_num == 0)
    {
      [str set :"0"];
    }
    else
    {
      [str format :"%d/%d", _num, _denom];
    }
  }
  else
  {
    if (rem == 0)
    {
      [str format :"%d", full];
    }
    else
    {
      [str format :"%d %d/%d", full, rem, _denom];
    }
  }
  
  return str;
}


//// Comparable protocol method

//
// Compare with another fraction (using doubles)
// 
// @param other   the other fraction
// 
// @return the compare result (-1,0,1)
// 
- (int) compare :(DFraction *) other
{
  int result = -1;
  
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    // Both fraction are normalized ..
     
    if ((_num == other->_num) && (_denom == other->_denom))
    {
      result = 0;
    }
    else
    {
      double dbl1 = [self  toDouble];
      double dbl2 = [other toDouble];
      
      if (dbl1 < dbl2)
      {
        result = -1;
      }
      else if (dbl1 > dbl2)
      {
        result = 1;
      }
      else // not likely
      {
        result = 0;
      }
    }
  }
  
  return result;
}

@end

/*===========================================================================*/

